﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System.IO;
using System.Threading.Tasks;

namespace Cngot.Extensions.Trace.Http
{
    internal class HttpMiddleware
    {
        private readonly HttpTraceOption _httpTraceOption;
        private readonly ILogEventPropertyFactory _dia;
        private readonly RequestDelegate _next;
        public HttpMiddleware(IOptions<HttpTraceOption> httpTraceOption, ILogEventPropertyFactory dia, RequestDelegate next)
        {
            _httpTraceOption = httpTraceOption.Value;
            _dia = dia;
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            _dia.SetTag("http.Method", httpContext.Request.Method);
            _dia.SetTag("http.Host", httpContext.Request.Host.ToString());
            _dia.SetTag("http.Url", httpContext.Request.Path + httpContext.Request.QueryString);
            _dia.SetTag("http.Header", _httpTraceOption.RequestHeaderDelegate(httpContext.Request));
            _dia.SetTag("http.ClientIp", httpContext.Request.HttpContext.Connection.RemoteIpAddress.ToString());

            if (_httpTraceOption.RequestBody)
            {
                httpContext.Request.EnableBuffering();
                string requestBody = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
                requestBody = Cut(requestBody, _httpTraceOption.MaxRequestBodySize);
                _dia.SetTag("http.Request", requestBody);
                httpContext.Request.Body.Position = 0;
            }
            if (!_httpTraceOption.ResponseBody)
            {
                await _next(httpContext);
            }
            else
            {
                httpContext.Request.Body.Position = 0;

                Stream originalBody = httpContext.Response.Body;

                using (var ms = new MemoryStream())
                {
                    httpContext.Response.Body = ms;

                    await _next(httpContext);

                    ms.Position = 0;
                    _dia.SetTag("http.Response", Cut(await new StreamReader(ms).ReadToEndAsync(), _httpTraceOption.MaxResponseBodySize));
                    ms.Position = 0;
                    await ms.CopyToAsync(originalBody);
                }

                httpContext.Response.Body = originalBody;
            }
        }

        private string Cut(string str, int maxSize)
        {
            if (string.IsNullOrEmpty(str) || str.Length <= maxSize)
            {
                return str ?? "";
            }
            else
            {
                return str.Substring(0, maxSize) + "...";
            }
        }
    }
}
